﻿
namespace SLPropertyGrid
{
	#region Using Directives
	using System;
	using System.Collections.Generic;
	using System.Linq;
	using System.Reflection;
	using System.Windows;
	using System.Windows.Controls;
	using System.Windows.Input;
	using System.Windows.Media;
	using System.Windows.Media.Imaging;
	using System.Diagnostics;
    using System.Threading;
	#endregion

	#region PropertyGrid
	/// <summary>
	/// PropertyGrid
	/// </summary>
	public partial class PropertyGrid : ValueEditorBase
	{
		#region Fields

		static Color aliceBlue = Color.FromArgb(255, 240, 248, 255);
		static Type thisType = typeof(PropertyGrid);

		ScrollViewer LayoutRoot;
		Grid mainGrid;

		#endregion

		#region Constructors
		/// <summary>
		/// Constructor
		/// </summary>
		public PropertyGrid()
		{
			LayoutRoot = new ScrollViewer();
			LayoutRoot.BorderBrush = new SolidColorBrush(Colors.LightGray);
			LayoutRoot.BorderThickness = new Thickness(1);
			LayoutRoot.HorizontalAlignment = HorizontalAlignment.Stretch;
			LayoutRoot.VerticalAlignment = VerticalAlignment.Stretch;
			LayoutRoot.HorizontalContentAlignment = HorizontalAlignment.Stretch;
			LayoutRoot.VerticalContentAlignment = VerticalAlignment.Stretch;
			LayoutRoot.HorizontalScrollBarVisibility = ScrollBarVisibility.Disabled;
			LayoutRoot.VerticalScrollBarVisibility = ScrollBarVisibility.Auto;
			this.Content = LayoutRoot;
		}
		/// <summary>
		/// Constructor
		/// </summary>
		public PropertyGrid(TextBox label, PropertyItem property)
			: base(label, property)
		{
			LayoutRoot = new ScrollViewer();
			LayoutRoot.Margin = new Thickness(0);
			LayoutRoot.Padding = new Thickness(0);
			LayoutRoot.BorderThickness = new Thickness(0);
			LayoutRoot.HorizontalAlignment = HorizontalAlignment.Stretch;
			LayoutRoot.VerticalAlignment = VerticalAlignment.Stretch;
			LayoutRoot.HorizontalContentAlignment = HorizontalAlignment.Stretch;
			LayoutRoot.VerticalContentAlignment = VerticalAlignment.Stretch;
			LayoutRoot.HorizontalScrollBarVisibility = ScrollBarVisibility.Disabled;
			LayoutRoot.VerticalScrollBarVisibility = ScrollBarVisibility.Auto;
			this.Content = LayoutRoot;

			SelectedObject = property.Value;
		}
		#endregion

		#region Properties

		#region SelectedObject

		public static readonly DependencyProperty SelectedObjectProperty =
		  DependencyProperty.Register("SelectedObject", typeof(object), thisType, new PropertyMetadata(null, OnSelectedObjectChanged));

		public object SelectedObject
		{
			get { return base.GetValue(SelectedObjectProperty); }
			set { base.SetValue(SelectedObjectProperty, value); }
		}

		private static void OnSelectedObjectChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
		{
			PropertyGrid propertyGrid = d as PropertyGrid;
			if (propertyGrid != null)
			{
				if (null != e.NewValue)
				{
					propertyGrid.ResetObject(e.NewValue);
				}
			}
		}
		#endregion

		#endregion

		#region Methods
		void ResetObject(object obj)
		{
			this.ResetMainGrid();

			#region state

			List<PropertyItem> props = PropertyGrid.ParseObject(obj);
			List<string> categories = new List<string>();
			RowDefinition rd;
			Border brd;
			Image img;
			TextBlock tbk;
			TextBox label;
			ValueEditorBase editor;

			int count = props.Count;
			int row = -1;
			string currentCategory = string.Empty;

			#endregion

			while (count > 0)
			{
				foreach (PropertyItem item in props)
				{
					#region Add Category Row
					if (!categories.Contains(item.Category))
					{
						#region Row Definition
						row++;
						rd = new RowDefinition();
						rd.Height = new GridLength(24);
						this.mainGrid.RowDefinitions.Add(rd);
						#endregion

						currentCategory = item.Category;
						categories.Add(item.Category);

						#region Column 0

						StackPanel stp = new StackPanel()
						{
							HorizontalAlignment = HorizontalAlignment.Center,
							VerticalAlignment = VerticalAlignment.Center
						};
						stp.Tag = item.Category;

						#region Hide
						img = new Image()
						{
							Source = new BitmapImage(new Uri("/minus.png", UriKind.Relative)),
							Height = 9,
							Width = 9,
							VerticalAlignment = VerticalAlignment.Center,
							HorizontalAlignment = HorizontalAlignment.Center
						};
						img.MouseLeftButtonUp += new MouseButtonEventHandler(CategoryHide_MouseLeftButtonUp);
						stp.Children.Add(img);
						#endregion

						#region Show
						img = new Image()
						{
                            Source = new BitmapImage(new Uri("/plus.png", UriKind.Relative)),
							Height = 9,
							Width = 9,
							Visibility = Visibility.Collapsed,
							VerticalAlignment = VerticalAlignment.Center,
							HorizontalAlignment = HorizontalAlignment.Center
						};
						img.MouseLeftButtonUp += new MouseButtonEventHandler(CategoryShow_MouseLeftButtonUp);
						stp.Children.Add(img);
						#endregion

						brd = new Border();
						brd.Background = new SolidColorBrush(aliceBlue);
						brd.Child = stp;

						this.mainGrid.Children.Add(brd);
						Grid.SetRow(brd, row);
						Grid.SetColumn(brd, 0);

						//<Border Grid.Column="0" Grid.Row="0" Background="AliceBlue">
						//    <StackPanel VerticalAlignment="Center" HorizontalAlignment="Center" >
						//        <Image Source="Assets/minus.png" Height="9" Width="9" VerticalAlignment="Center" HorizontalAlignment="Center" MouseLeftButtonUp="CategoryHide_MouseLeftButtonUp" />
						//        <Image Source="Assets/plus.png" Height="9" Width="9" VerticalAlignment="Center" HorizontalAlignment="Center" Visibility="Collapsed" MouseLeftButtonUp="CategoryShow_MouseLeftButtonUp" />
						//    </StackPanel>
						//</Border>

						#endregion

						#region Column 1 & 2

						tbk = new TextBlock()
						{
							Text = item.Category,
							VerticalAlignment = VerticalAlignment.Center,
							HorizontalAlignment = HorizontalAlignment.Left
						};
						Canvas.SetZIndex(tbk, 2);

						brd = new Border();
						brd.Background = new SolidColorBrush(aliceBlue);
						brd.Child = tbk;

						this.mainGrid.Children.Add(brd);
						Grid.SetRow(brd, row);
						Grid.SetColumn(brd, 1);
						Grid.SetColumnSpan(brd, 2);

						//<Border Grid.Column="1" Grid.ColumnSpan="2" Grid.Row="0" Background="AliceBlue" Canvas.ZIndex="2">
						//    <TextBlock Text="Component Detail" VerticalAlignment="Center" HorizontalAlignment="Left" />
						//</Border>

						#endregion

						break;
					}
					#endregion
				}

				// Get the properties for the current category
				var catProperties = from p in props
									where p.Category == currentCategory
									select p;

				// Add a row for each property in the current category
				foreach (PropertyItem item in catProperties)
				{
                    if (!item.CanWrite)
                        continue;

                    //Debug.WriteLine(" ---------------------------");
                    //Debug.WriteLine("Label: " + item.DisplayName);
                    //Debug.WriteLine("Name: " + item.Name);

					count--;

					#region Row Definition
					row++;
					rd = new RowDefinition();
					this.mainGrid.RowDefinitions.Add(rd);
					#endregion

					#region Column 0 - Margin

					brd = GetBorder(item.Category);
					this.mainGrid.Children.Add(brd);
					Grid.SetRow(brd, row);
					Grid.SetColumn(brd, 0);

					#endregion

					#region Column 1 - Label

					label = GetLabel(item.Name, item.DisplayName);

					editor = EditorService.GetEditor(item, label);

					if (editor is PropertyGrid)
					{
						#region Nested Object
						brd = GetBorder(item.Category);
						brd.BorderThickness = new Thickness(0, 0, 0, 1);
						brd.Child = editor;
						this.mainGrid.Children.Add(brd);
						Grid.SetRow(brd, row);
						Grid.SetColumn(brd, 1);
						Grid.SetColumnSpan(brd, 2);
						#endregion
					}
					else
					{
						brd = GetBorder(label, item.Category);
						this.mainGrid.Children.Add(brd);
						Grid.SetRow(brd, row);
						Grid.SetColumn(brd, 1);

						#region Column 2 - Value

						brd = null;
						brd = new Border()
						{
							Margin = new Thickness(1, 0, 0, 0),
							BorderThickness = new Thickness(1, 0, 0, 1),
							BorderBrush = new SolidColorBrush(aliceBlue)
						};
						brd.Child = editor;
						brd.Tag = item.Category;
						this.mainGrid.Children.Add(brd);
						Grid.SetRow(brd, row);
						Grid.SetColumn(brd, 2);

						#endregion

					}

					#endregion

				}

			}

            // HEREHERE
            //GridSplitter gsp = new GridSplitter()
            //{
            //    IsTabStop = false,
            //    HorizontalAlignment = HorizontalAlignment.Left,
            //    VerticalAlignment = VerticalAlignment.Stretch,
            //    Background = new SolidColorBrush(aliceBlue),
            //    ShowsPreview = false,
            //    Width = 2
            //};
            //Grid.SetColumn(gsp, 2);
            //Grid.SetRowSpan(gsp, row);
            //Canvas.SetZIndex(gsp, 1);
            //this.mainGrid.Children.Add(gsp);

		}
		void ResetMainGrid()
		{
			// Clear the grid
			if (null != this.mainGrid)
			{
				this.mainGrid.Children.Clear();
				this.mainGrid.RowDefinitions.Clear();
			}
			else
			{
				this.mainGrid = new Grid();
				this.mainGrid.VerticalAlignment = VerticalAlignment.Top;

				ColumnDefinition cd;

				//<Grid.ColumnDefinitions>
				//    <ColumnDefinition Width="18"/>
				//    <ColumnDefinition Width="100" MinWidth="50" />
				//    <ColumnDefinition Width="*"/>
				//</Grid.ColumnDefinitions>

				cd = new ColumnDefinition();
				cd.Width = new GridLength(18);
				this.mainGrid.ColumnDefinitions.Add(cd);

				cd = new ColumnDefinition();
				cd.Width = new GridLength(200);
				cd.MinWidth = 50;
				this.mainGrid.ColumnDefinitions.Add(cd);

				cd = new ColumnDefinition();
				this.mainGrid.ColumnDefinitions.Add(cd);

				// Add to the control
				this.LayoutRoot.Content = this.mainGrid;

			}
		}
		void ToggleCategoryVisible(bool show, string category)
		{
			foreach (FrameworkElement element in this.mainGrid.Children)
			{
				object value = element.Tag;
				if (null != value)
				{
					string tag = (string)value;
					if (category == tag)
						element.Visibility = show ? Visibility.Visible : Visibility.Collapsed;
				}
			}
		}
		static List<PropertyItem> ParseObject(object objItem)
		{
			List<PropertyItem> pc = new List<PropertyItem>();
			Type t = objItem.GetType();
			var props = t.GetProperties();

			foreach (PropertyInfo pinfo in props)
			{
                var v = pinfo.GetIndexParameters();

                if (v != null && v.Length > 0)
                    continue;

                if (!pinfo.CanWrite || !pinfo.CanRead)
                    continue;

				PropertyItem prop = new PropertyItem(objItem, pinfo);
				pc.Add(prop);
			}

			return pc;
		}

        public static int _counter2 = 0;

		static TextBox GetLabel(string name, string displayName)
		{
            int unique = Interlocked.Increment(ref _counter2);

			TextBox label = new TextBox()
			{
				Name = "lbl" + name + unique.ToString(),
				Text = displayName,
				IsReadOnly = true,
				BorderThickness = new Thickness(0),
				Background = new SolidColorBrush(Colors.White),
				Margin = new Thickness(0),
				Cursor = Cursors.Arrow,
				VerticalAlignment = VerticalAlignment.Center,
				HorizontalAlignment = HorizontalAlignment.Stretch
			};
			return label;
		}
		Border GetBorder(TextBox label, string category)
		{
			Border brd = GetBorder(category);
			brd.BorderThickness = new Thickness(0, 0, 0, 1);
			brd.Child = label;
			return brd;
		}
		Border GetBorder(string category)
		{
			Border brd = new Border()
			{
				BorderBrush = new SolidColorBrush(aliceBlue),
				Tag = category
			};
			return brd;
		}
		#endregion

		#region Event Handlers
		private void CategoryHide_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
		{
			FrameworkElement ctl = sender as FrameworkElement;
			StackPanel stp = ctl.Parent as StackPanel;
			string cat = (string)stp.Tag;
			stp.Children[0].Visibility = Visibility.Collapsed;
			stp.Children[1].Visibility = Visibility.Visible;
			this.Dispatcher.BeginInvoke(delegate()
			{
				ToggleCategoryVisible(false, cat);
			});
		}
		private void CategoryShow_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
		{
			FrameworkElement ctl = sender as FrameworkElement;
			StackPanel stp = ctl.Parent as StackPanel;
			string cat = (string)stp.Tag;
			stp.Children[0].Visibility = Visibility.Visible;
			stp.Children[1].Visibility = Visibility.Collapsed;
			this.Dispatcher.BeginInvoke(delegate()
			{
				ToggleCategoryVisible(true, cat);
			});
		}
		#endregion
	}
	#endregion
}